import numpy as np
from metrics import accuracy_score

class  LogisticRegression:
    def __init__(self):
        """初始化Logistic Regression模型"""
        self.coef_=None #系数，θ1-θn
        self.interception_=None #截距，θ0
        self._theta=None #整体的θ列向量
#无正规化方程解，不使用随机梯度下降
#使用批量梯度下降    
    def _sigmoid(self,t):
        return 1./(1.+np.exp(-t))

    def fit(self,X_train,y_train,eta=0.01,n_inters=1e4):
        """根据训练集X_train,y_train,使用梯度下降法训练Linear Rregression模型"""
        assert X_train.shape[0]==y_train.shape[0]
        def J(theta,X_b,y):#损失函数
            y_hat=self._sigmoid(X_b.dot(theta))
            try:
                return -np.sum(y*np.log(y_hat)+(1-y)*np.log(1-y_hat))/len(y)
            except:
                return float("inf")
        def dJ(theta,X_b,y):#损失函数的梯度函数

            return X_b.T.dot(self._sigmoid(X_b.dot(theta))-y)/len(X_b)#向量化计算：
        def gradient_descent(X_b,y,initail_theta,eta,n_iters=1e4,epsilon=1e-8):
            theta=initail_theta
            cur_iter=0
            while cur_iter<n_iters:
                gradient=dJ(theta,X_b,y)
                last_theta=theta
                theta=theta-eta*gradient
                if (abs(J(theta,X_b,y)-J(last_theta,X_b,y))<epsilon):
                    break
                cur_iter=cur_iter+1
            return theta
        X_b=np.hstack([np.ones((len(X_train),1)),X_train])
        initial_theta=np.zeros(X_b.shape[1])#初始化所有参数是0
        self._theta=gradient_descent(X_b,y_train,initial_theta,eta,n_inters)#梯度下降训练
        self.interception_=self._theta[0]
        self.coef_=self._theta[1:]
        return self
        

    """判断给定的样本属于哪个类别的概率"""
    def predict_proba(self,x_predict):
        assert self.coef_ is not None and self.interception_ is not None
        #保证输入数据集的列数（特征）等于特征个数
        assert x_predict.shape[1]==self.coef_.shape[0]
        #拼接
        X_b=np.hstack([np.ones((len(x_predict),1)),x_predict])
        return self._sigmoid(X_b.dot(self._theta))#逻辑回归表示的概率用sigmoid

    def predict(self,x_predict):
        assert self.coef_ is not None and self.interception_ is not None
        #保证输入数据集的列数（特征）等于特征个数
        assert x_predict.shape[1]==self.coef_.shape[0]
        #拼接
        X_b=np.hstack([np.ones((len(x_predict),1)),x_predict])
        proba=self.predict_proba(x_predict)
        return np.array(proba>=0.5,dtype="int")#proba概率大于等于0.5为1，反之为0
    
    def score(self,x_test,y_test):
        y_predict=self.predict(x_test)
        return accuracy_score(y_test,y_predict)#分类问题看准确度
    
    def __reper__(self):
        return "线性回归模型"